package org.springmole.business.dao.impl;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.SessionFactory;
import org.hibernate.stat.CollectionStatistics;
import org.hibernate.stat.EntityStatistics;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springmole.business.bean.Result;
import org.springmole.business.bean.ResultStatus;
import org.springmole.business.bean.hibernate.CollectionData;
import org.springmole.business.bean.hibernate.EntityData;
import org.springmole.business.bean.hibernate.SecondLevelData;
import org.springmole.business.bean.hibernate.HibernateData;
import org.springmole.business.bean.hibernate.HibernateResultData;
import org.springmole.business.bean.hibernate.QueryData;
import org.springmole.business.bean.hibernate.SummaryData;
import org.springmole.business.dao.HibernateDao;


@Repository
public class HibernateDaoImpl implements HibernateDao {

	@Autowired
	private SessionFactory sessionFactory;

	
	@Override
	public Result<HibernateResultData<EntityData>> getEntityStatistic() {
		AbstractHibernateStatisticParser<EntityData> parser = new AbstractHibernateStatisticParser<EntityData>(){

			@Override
			protected List<EntityData> parse(Statistics statistics) {
				List<EntityData> entityDataList = new ArrayList<EntityData>();
				String[] entityNameArray = statistics.getEntityNames();
				for (String entityName : entityNameArray) {
					EntityStatistics entityStatistics = statistics.getEntityStatistics(entityName);
					
					EntityData entityData = new EntityData();
					
					BeanUtils.copyProperties(entityStatistics, entityData);
					
					entityDataList.add(entityData);
				}
				
				return entityDataList;
			} // parse
			
		};
		
		return parser.parseStatistic();
	}

	
	@Override
	public Result<HibernateResultData<CollectionData>> getCollectionStatistic() {
		AbstractHibernateStatisticParser<CollectionData> parser = new AbstractHibernateStatisticParser<CollectionData>() {

			@Override
			protected List<CollectionData> parse(Statistics statistics) {
				List<CollectionData> collectionDataList = new ArrayList<CollectionData>();
				
				String[] collectionRoleNameArray = statistics.getCollectionRoleNames();
				for (String collectionRoleName : collectionRoleNameArray) {
					CollectionStatistics collectionStatistics = statistics.getCollectionStatistics(collectionRoleName);
					CollectionData collectionData = new CollectionData();
					
					BeanUtils.copyProperties(collectionStatistics, collectionData);
					
					collectionDataList.add(collectionData);
				}
				
				return collectionDataList;
			} // parse

		};
		
		return parser.parseStatistic();
	}

	
	@Override
	public Result<HibernateResultData<QueryData>> getQueryStatistic() {
		AbstractHibernateStatisticParser<QueryData> parser = new AbstractHibernateStatisticParser<QueryData>(){
			
			@Override
			protected List<QueryData> parse(Statistics statistics) {
				List<QueryData> queryDataList = new ArrayList<QueryData>();
				
				String[] queryNameArray = statistics.getQueries();
				for (String regionName : queryNameArray) {
					QueryStatistics queryStatistics = statistics.getQueryStatistics(regionName);
					QueryData queryData = new QueryData();
					
					BeanUtils.copyProperties(queryStatistics, queryData);
					
					queryDataList.add(queryData);
				}
				
				return queryDataList;
			} // parse
			
		};
			
		return parser.parseStatistic();
	}
	
	
	@Override
	public Result<HibernateResultData<SecondLevelData>> getSecondLevelDataStatistics() {
		AbstractHibernateStatisticParser<SecondLevelData> parser = new AbstractHibernateStatisticParser<SecondLevelData>(){
			
			@Override
			protected List<SecondLevelData> parse(Statistics statistics) {
				List<SecondLevelData> secondLevelCacheDataList = new ArrayList<SecondLevelData>();
				
				String[] regionNameArray = statistics.getSecondLevelCacheRegionNames();
				for (String regionName : regionNameArray) {
					SecondLevelCacheStatistics secondLevelCacheStatistics = statistics.getSecondLevelCacheStatistics(regionName);
					SecondLevelData secondLevelCacheData = new SecondLevelData();
					
					BeanUtils.copyProperties(secondLevelCacheStatistics, secondLevelCacheData);
							
					secondLevelCacheDataList.add(secondLevelCacheData);
				}
				
				return secondLevelCacheDataList;
			} // parse
			
		};
		
		return parser.parseStatistic();
	}
	

	@Override
	public Result<HibernateResultData<SummaryData>> getSummaryDataStatistics() {
		AbstractHibernateStatisticParser<SummaryData> parser = new AbstractHibernateStatisticParser<SummaryData>(){
			
			@Override
			protected List<SummaryData> parse(Statistics statistics) {
				List<SummaryData> summaryDataList = new ArrayList<SummaryData>();
				
				SummaryData summaryData = new SummaryData();
				
				BeanUtils.copyProperties(statistics, summaryData);
				
				// Initialise the grouping of all properties under specific list for xml generation. 
				summaryData.init();
				
				summaryDataList.add(summaryData);
				
				return summaryDataList;
			} // parse
			
		};
		
		return parser.parseStatistic();
	}
	
	
	/**
	 * Generate the base object structure for a hibernate xml response.
	 * The implementation of the method parse must provide the body of the response. <br/>
	 * 
	 * <Result>
	 * 	<ResultStatus success="">
	 * 		<ErrorList/>
	 * 	</ResultStatus>
	 * 	<ResultData name="statistics">
	 * 		<HibernateDataList>
	 * 			{response body}
	 * 		</HibernateDataList>
	 * 	</ResultData>
	 * </Result>
	 *   
	 * @author cake
	 * @param <T>
	 */
	private abstract class AbstractHibernateStatisticParser<T extends HibernateData> {

		protected abstract List<T> parse(Statistics statistics);
		
		public Result<HibernateResultData<T>> parseStatistic() {
			Result<HibernateResultData<T>> result = new Result<HibernateResultData<T>>();
			HibernateResultData<T> hibernateResultData = new HibernateResultData<T>("statistics");
			ResultStatus resultStatus = new ResultStatus();
			
			Statistics statistics = sessionFactory.getStatistics();
			hibernateResultData.setHibernateDataList(parse(statistics));
			
			resultStatus.setSuccess(true);
			result.setResultStatus(resultStatus);
			result.setResultData(hibernateResultData);			
			return result;
		}
	}


}
